package com.jltx.storage.coroutinesdownloader

import android.content.Context
import android.util.Log
import com.jltx.storage.coroutinesdownloader.net.DownloadFileMultiCall
import com.jltx.storage.coroutinesdownloader.util.OkhttpClientFactoryUtils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.channels.consumeEach
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.converter.scalars.ScalarsConverterFactory
import kotlin.coroutines.CoroutineContext

/**
 * @author jltxseo
 *         Created by junlintianxia on 2020/04/15.
 */
internal object Downloader : CoroutineScope {
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main

    private var context: Context? = null
    private var mRetrofit: Retrofit? = null
    private var apiService: BaseApiService? = null

    fun init(context: Context) {
        this.context = context.applicationContext
    }

    fun getContext(): Context? {
        return context
    }

    /**
     * 使用前必须初始化
     *
     * @param okHttpClient
     */
    fun initHttpClient(okHttpClient: OkHttpClient?) {
        if (mRetrofit == null && okHttpClient != null) {
            val builder = Retrofit.Builder()
                .baseUrl("http://www.baidu.com")
                .addConverterFactory(ScalarsConverterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
            builder.client(okHttpClient)
            mRetrofit = builder.build()
        }
    }

    /**
     * create you ApiService
     * Create an implementation of the API endpoints defined by the `service` interface.
     */
    fun <T> create(service: Class<T>?): T {
        if (service == null) {
            throw RuntimeException("Api service is null!")
        }
        return mRetrofit!!.create(service)
    }

    /**
     * create BaseApi  defalte ApiManager
     *
     * @return ApiManager
     */
    fun createBaseApi(): Downloader? {
        if (mRetrofit == null) {
            initHttpClient(OkhttpClientFactoryUtils.okHttpClient)
        }
        apiService = create(BaseApiService::class.java)
        return this
    }

    /**
     * 获取带进度的单个下载的Observable
     * 进度会在onNext回调多次
     *
     * @param url
     * @param localPath
     * @return
     */
    fun downloadReceiveChannel(
        url: String?,
        localPath: String?
    ): ReceiveChannel<RequestResult> {
        if (apiService == null) {
            createBaseApi()
        }
        //初始网络请求放到IO线程
        return DownloadFileMultiCall().convertResponseBodyToRequestResult(
            url,
            localPath,
            apiService
        )
    }

    /**
     * 下载单个文件
     *
     * @param tag          标记下载任务的tag
     * @param url          要下载的文件的url链接
     * @param localPath    要下载的文件输出本地全路径
     * @param fileCallback 回调监听器
     */
    fun download(
        tag: Any?,
        url: String?,
        localPath: String?,
        fileCallback: FileCallback<RequestResult>?
    ) {
        var job = launch {
            //开始下载
            if (apiService == null) {
                createBaseApi()
            }
            Log.d("HHHHH", "download start Thread: ${Thread.currentThread()}")
            fileCallback?.onStart(tag)
            //去网络下载
            val channel = downloadReceiveChannel(url, localPath)
            channel.consumeEach {
                //进行页面数据的更新
                Log.d("HHHHH", "request: $it thread: ${Thread.currentThread()}")
                when (it.status) {
                    RequestStatus.STATUS_LOADING -> {
                        fileCallback?.onLoading(tag, it)
                    }
                    RequestStatus.STATUS_SUCCESS -> {
                        fileCallback?.onSuccess(tag, it)
                    }
                    else -> {
                        fileCallback?.onFailure(tag, it)
                    }
                }
            }
            Log.d("HHHHH", "download End Thread: ${Thread.currentThread()}")
        }
    }
}